<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js"><span id='Ext-Class'>/**
</span> * @author Jacky Nguyen &lt;jacky@sencha.com&gt;
 * @docauthor Jacky Nguyen &lt;jacky@sencha.com&gt;
 * @class Ext.Class
 * 
 * Handles class creation throughout the whole framework. Note that most of the time {@link Ext#define Ext.define} should
 * be used instead, since it's a higher level wrapper that aliases to {@link Ext.ClassManager#create}
 * to enable namespacing and dynamic dependency resolution.
 * 
 * # Basic syntax: #
 * 
 *     Ext.define(className, properties);
 * 
 * in which `properties` is an object represent a collection of properties that apply to the class. See
 * {@link Ext.ClassManager#create} for more detailed instructions.
 * 
 *     Ext.define('Person', {
 *          name: 'Unknown',
 * 
 *          constructor: function(name) {
 *              if (name) {
 *                  this.name = name;
 *              }
 * 
 *              return this;
 *          },
 * 
 *          eat: function(foodType) {
 *              alert(&quot;I'm eating: &quot; + foodType);
 * 
 *              return this;
 *          }
 *     });
 * 
 *     var aaron = new Person(&quot;Aaron&quot;);
 *     aaron.eat(&quot;Sandwich&quot;); // alert(&quot;I'm eating: Sandwich&quot;);
 * 
 * Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
 * everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
 * 
 * # Inheritance: #
 * 
 *     Ext.define('Developer', {
 *          extend: 'Person',
 * 
 *          constructor: function(name, isGeek) {
 *              this.isGeek = isGeek;
 * 
 *              // Apply a method from the parent class' prototype
 *              this.callParent([name]);
 * 
 *              return this;
 * 
 *          },
 * 
 *          code: function(language) {
 *              alert(&quot;I'm coding in: &quot; + language);
 * 
 *              this.eat(&quot;Bugs&quot;);
 * 
 *              return this;
 *          }
 *     });
 * 
 *     var jacky = new Developer(&quot;Jacky&quot;, true);
 *     jacky.code(&quot;JavaScript&quot;); // alert(&quot;I'm coding in: JavaScript&quot;);
 *                               // alert(&quot;I'm eating: Bugs&quot;);
 * 
 * See {@link Ext.Base#callParent} for more details on calling superclass' methods
 * 
 * # Mixins: #
 * 
 *     Ext.define('CanPlayGuitar', {
 *          playGuitar: function() {
 *             alert(&quot;F#...G...D...A&quot;);
 *          }
 *     });
 * 
 *     Ext.define('CanComposeSongs', {
 *          composeSongs: function() { ... }
 *     });
 * 
 *     Ext.define('CanSing', {
 *          sing: function() {
 *              alert(&quot;I'm on the highway to hell...&quot;)
 *          }
 *     });
 * 
 *     Ext.define('Musician', {
 *          extend: 'Person',
 * 
 *          mixins: {
 *              canPlayGuitar: 'CanPlayGuitar',
 *              canComposeSongs: 'CanComposeSongs',
 *              canSing: 'CanSing'
 *          }
 *     })
 * 
 *     Ext.define('CoolPerson', {
 *          extend: 'Person',
 * 
 *          mixins: {
 *              canPlayGuitar: 'CanPlayGuitar',
 *              canSing: 'CanSing'
 *          },
 * 
 *          sing: function() {
 *              alert(&quot;Ahem....&quot;);
 * 
 *              this.mixins.canSing.sing.call(this);
 * 
 *              alert(&quot;[Playing guitar at the same time...]&quot;);
 * 
 *              this.playGuitar();
 *          }
 *     });
 * 
 *     var me = new CoolPerson(&quot;Jacky&quot;);
 * 
 *     me.sing(); // alert(&quot;Ahem...&quot;);
 *                // alert(&quot;I'm on the highway to hell...&quot;);
 *                // alert(&quot;[Playing guitar at the same time...]&quot;);
 *                // alert(&quot;F#...G...D...A&quot;);
 * 
 * # Config: #
 * 
 *     Ext.define('SmartPhone', {
 *          config: {
 *              hasTouchScreen: false,
 *              operatingSystem: 'Other',
 *              price: 500
 *          },
 * 
 *          isExpensive: false,
 * 
 *          constructor: function(config) {
 *              this.initConfig(config);
 * 
 *              return this;
 *          },
 * 
 *          applyPrice: function(price) {
 *              this.isExpensive = (price &gt; 500);
 * 
 *              return price;
 *          },
 * 
 *          applyOperatingSystem: function(operatingSystem) {
 *              if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
 *                  return 'Other';
 *              }
 * 
 *              return operatingSystem;
 *          }
 *     });
 * 
 *     var iPhone = new SmartPhone({
 *          hasTouchScreen: true,
 *          operatingSystem: 'iOS'
 *     });
 * 
 *     iPhone.getPrice(); // 500;
 *     iPhone.getOperatingSystem(); // 'iOS'
 *     iPhone.getHasTouchScreen(); // true;
 *     iPhone.hasTouchScreen(); // true
 * 
 *     iPhone.isExpensive; // false;
 *     iPhone.setPrice(600);
 *     iPhone.getPrice(); // 600
 *     iPhone.isExpensive; // true;
 * 
 *     iPhone.setOperatingSystem('AlienOS');
 *     iPhone.getOperatingSystem(); // 'Other'
 * 
 * # Statics: #
 * 
 *     Ext.define('Computer', {
 *          statics: {
 *              factory: function(brand) {
 *                 // 'this' in static methods refer to the class itself
 *                  return new this(brand);
 *              }
 *          },
 * 
 *          constructor: function() { ... }
 *     });
 * 
 *     var dellComputer = Computer.factory('Dell');
 * 
 * Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
 * static properties within class methods
 *
 */
(function() {

    var Class,
        Base = Ext.Base,
        baseStaticProperties = [],
        baseStaticProperty;

    for (baseStaticProperty in Base) {
        if (Base.hasOwnProperty(baseStaticProperty)) {
            baseStaticProperties.push(baseStaticProperty);
        }
    }

<span id='Ext-Class-method-constructor'>    /**
</span>     * @method constructor
     * Creates new class.
     * @param {Object} classData An object represent the properties of this class
     * @param {Function} createdFn Optional, the callback function to be executed when this class is fully created.
     * Note that the creation process can be asynchronous depending on the pre-processors used.
     * @return {Ext.Base} The newly created class
     */
    Ext.Class = Class = function(newClass, classData, onClassCreated) {
        if (typeof newClass !== 'function') {
            onClassCreated = classData;
            classData = newClass;
            newClass = function() {
                return this.constructor.apply(this, arguments);
            };
        }

        if (!classData) {
            classData = {};
        }

        var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(),
            registeredPreprocessors = Class.getPreprocessors(),
            index = 0,
            preprocessors = [],
            preprocessor, preprocessors, staticPropertyName, process, i, j, ln;

        for (i = 0, ln = baseStaticProperties.length; i &lt; ln; i++) {
            staticPropertyName = baseStaticProperties[i];
            newClass[staticPropertyName] = Base[staticPropertyName];
        }

        delete classData.preprocessors;

        for (j = 0, ln = preprocessorStack.length; j &lt; ln; j++) {
            preprocessor = preprocessorStack[j];

            if (typeof preprocessor === 'string') {
                preprocessor = registeredPreprocessors[preprocessor];

                if (!preprocessor.always) {
                    if (classData.hasOwnProperty(preprocessor.name)) {
                        preprocessors.push(preprocessor.fn);
                    }
                }
                else {
                    preprocessors.push(preprocessor.fn);
                }
            }
            else {
                preprocessors.push(preprocessor);
            }
        }

        classData.onClassCreated = onClassCreated;

        classData.onBeforeClassCreated = function(cls, data) {
            onClassCreated = data.onClassCreated;

            delete data.onBeforeClassCreated;
            delete data.onClassCreated;

            cls.implement(data);

            if (onClassCreated) {
                onClassCreated.call(cls, cls);
            }
        };

        process = function(cls, data) {
            preprocessor = preprocessors[index++];

            if (!preprocessor) {
                data.onBeforeClassCreated.apply(this, arguments);
                return;
            }

            if (preprocessor.call(this, cls, data, process) !== false) {
                process.apply(this, arguments);
            }
        };

        process.call(Class, newClass, classData);

        return newClass;
    };

    Ext.apply(Class, {

<span id='Ext-Class-property-preprocessors'>        /** @private */
</span>        preprocessors: {},

<span id='Ext-Class-method-registerPreprocessor'>        /**
</span>         * Register a new pre-processor to be used during the class creation process
         *
         * @member Ext.Class registerPreprocessor
         * @param {String} name The pre-processor's name
         * @param {Function} fn The callback function to be executed. Typical format:

    function(cls, data, fn) {
        // Your code here

        // Execute this when the processing is finished.
        // Asynchronous processing is perfectly ok
        if (fn) {
            fn.call(this, cls, data);
        }
    });

         * Passed arguments for this function are:
         *
         * - `{Function} cls`: The created class
         * - `{Object} data`: The set of properties passed in {@link Ext.Class} constructor
         * - `{Function} fn`: The callback function that &lt;b&gt;must&lt;/b&gt; to be executed when this pre-processor finishes,
         * regardless of whether the processing is synchronous or aynchronous
         *
         * @return {Ext.Class} this
         * @markdown
         */
        registerPreprocessor: function(name, fn, always) {
            this.preprocessors[name] = {
                name: name,
                always: always ||  false,
                fn: fn
            };

            return this;
        },

<span id='Ext-Class-method-getPreprocessor'>        /**
</span>         * Retrieve a pre-processor callback function by its name, which has been registered before
         *
         * @param {String} name
         * @return {Function} preprocessor
         */
        getPreprocessor: function(name) {
            return this.preprocessors[name];
        },

        getPreprocessors: function() {
            return this.preprocessors;
        },

<span id='Ext-Class-method-getDefaultPreprocessors'>        /**
</span>         * Retrieve the array stack of default pre-processors
         *
         * @return {Function} defaultPreprocessors
         */
        getDefaultPreprocessors: function() {
            return this.defaultPreprocessors || [];
        },

<span id='Ext-Class-method-setDefaultPreprocessors'>        /**
</span>         * Set the default array stack of default pre-processors
         *
         * @param {Array} preprocessors
         * @return {Ext.Class} this
         */
        setDefaultPreprocessors: function(preprocessors) {
            this.defaultPreprocessors = Ext.Array.from(preprocessors);

            return this;
        },

<span id='Ext-Class-method-setDefaultPreprocessorPosition'>        /**
</span>         * Insert this pre-processor at a specific position in the stack, optionally relative to
         * any existing pre-processor. For example:

    Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
        // Your code here

        if (fn) {
            fn.call(this, cls, data);
        }
    }).insertDefaultPreprocessor('debug', 'last');

         * @param {String} name The pre-processor name. Note that it needs to be registered with
         * {@link Ext#registerPreprocessor registerPreprocessor} before this
         * @param {String} offset The insertion position. Four possible values are:
         * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
         * @param {String} relativeName
         * @return {Ext.Class} this
         * @markdown
         */
        setDefaultPreprocessorPosition: function(name, offset, relativeName) {
            var defaultPreprocessors = this.defaultPreprocessors,
                index;

            if (typeof offset === 'string') {
                if (offset === 'first') {
                    defaultPreprocessors.unshift(name);

                    return this;
                }
                else if (offset === 'last') {
                    defaultPreprocessors.push(name);

                    return this;
                }

                offset = (offset === 'after') ? 1 : -1;
            }

            index = Ext.Array.indexOf(defaultPreprocessors, relativeName);

            if (index !== -1) {
                Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
            }

            return this;
        }
    });

<span id='Ext-Class-cfg-extend'>    /**
</span>     * @cfg {String} extend
     * The parent class that this class extends. For example:
     *
     *     Ext.define('Person', {
     *         say: function(text) { alert(text); }
     *     });
     *
     *     Ext.define('Developer', {
     *         extend: 'Person',
     *         say: function(text) { this.callParent([&quot;print &quot;+text]); }
     *     });
     */
    Class.registerPreprocessor('extend', function(cls, data) {
        var extend = data.extend,
            base = Ext.Base,
            basePrototype = base.prototype,
            prototype = function() {},
            parent, i, k, ln, staticName, parentStatics,
            parentPrototype, clsPrototype;

        if (extend &amp;&amp; extend !== Object) {
            parent = extend;
        }
        else {
            parent = base;
        }

        parentPrototype = parent.prototype;

        prototype.prototype = parentPrototype;
        clsPrototype = cls.prototype = new prototype();

        if (!('$class' in parent)) {
            for (i in basePrototype) {
                if (!parentPrototype[i]) {
                    parentPrototype[i] = basePrototype[i];
                }
            }
        }

        clsPrototype.self = cls;

        cls.superclass = clsPrototype.superclass = parentPrototype;

        delete data.extend;

        // Statics inheritance
        parentStatics = parentPrototype.$inheritableStatics;

        if (parentStatics) {
            for (k = 0, ln = parentStatics.length; k &lt; ln; k++) {
                staticName = parentStatics[k];

                if (!cls.hasOwnProperty(staticName)) {
                    cls[staticName] = parent[staticName];
                }
            }
        }

        // Merge the parent class' config object without referencing it
        if (parentPrototype.config) {
            clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
        }
        else {
            clsPrototype.config = {};
        }

        if (clsPrototype.$onExtended) {
            clsPrototype.$onExtended.call(cls, cls, data);
        }

        if (data.onClassExtended) {
            clsPrototype.$onExtended = data.onClassExtended;
            delete data.onClassExtended;
        }

    }, true);

<span id='Ext-Class-cfg-statics'>    /**
</span>     * @cfg {Object} statics
     * List of static methods for this class. For example:
     *
     *     Ext.define('Computer', {
     *          statics: {
     *              factory: function(brand) {
     *                  // 'this' in static methods refer to the class itself
     *                  return new this(brand);
     *              }
     *          },
     *
     *          constructor: function() { ... }
     *     });
     *
     *     var dellComputer = Computer.factory('Dell');
     */
    Class.registerPreprocessor('statics', function(cls, data) {
        var statics = data.statics,
            name;

        for (name in statics) {
            if (statics.hasOwnProperty(name)) {
                cls[name] = statics[name];
            }
        }

        delete data.statics;
    });

<span id='Ext-Class-cfg-inheritableStatics'>    /**
</span>     * @cfg {Object} inheritableStatics
     * List of inheritable static methods for this class.
     * Otherwise just like {@link #statics} but subclasses inherit these methods.
     */
    Class.registerPreprocessor('inheritableStatics', function(cls, data) {
        var statics = data.inheritableStatics,
            inheritableStatics,
            prototype = cls.prototype,
            name;

        inheritableStatics = prototype.$inheritableStatics;

        if (!inheritableStatics) {
            inheritableStatics = prototype.$inheritableStatics = [];
        }

        for (name in statics) {
            if (statics.hasOwnProperty(name)) {
                cls[name] = statics[name];
                inheritableStatics.push(name);
            }
        }

        delete data.inheritableStatics;
    });

<span id='Ext-Class-cfg-mixins'>    /**
</span>     * @cfg {Object} mixins
     * List of classes to mix into this class. For example:
     *
     *     Ext.define('CanSing', {
     *          sing: function() {
     *              alert(&quot;I'm on the highway to hell...&quot;)
     *          }
     *     });
     *
     *     Ext.define('Musician', {
     *          extend: 'Person',
     *
     *          mixins: {
     *              canSing: 'CanSing'
     *          }
     *     })
     */
    Class.registerPreprocessor('mixins', function(cls, data) {
        cls.mixin(data.mixins);

        delete data.mixins;
    });

<span id='Ext-Class-cfg-config'>    /**
</span>     * @cfg {Object} config
     * List of configuration options with their default values, for which automatically
     * accessor methods are generated.  For example:
     *
     *     Ext.define('SmartPhone', {
     *          config: {
     *              hasTouchScreen: false,
     *              operatingSystem: 'Other',
     *              price: 500
     *          },
     *          constructor: function(cfg) {
     *              this.initConfig(cfg);
     *          }
     *     });
     *
     *     var iPhone = new SmartPhone({
     *          hasTouchScreen: true,
     *          operatingSystem: 'iOS'
     *     });
     *
     *     iPhone.getPrice(); // 500;
     *     iPhone.getOperatingSystem(); // 'iOS'
     *     iPhone.getHasTouchScreen(); // true;
     *     iPhone.hasTouchScreen(); // true
     */
    Class.registerPreprocessor('config', function(cls, data) {
        var prototype = cls.prototype;

        Ext.Object.each(data.config, function(name) {
            var cName = name.charAt(0).toUpperCase() + name.substr(1),
                pName = name,
                apply = 'apply' + cName,
                setter = 'set' + cName,
                getter = 'get' + cName;

            if (!(apply in prototype) &amp;&amp; !data.hasOwnProperty(apply)) {
                data[apply] = function(val) {
                    return val;
                };
            }

            if (!(setter in prototype) &amp;&amp; !data.hasOwnProperty(setter)) {
                data[setter] = function(val) {
                    var ret = this[apply].call(this, val, this[pName]);

                    if (ret !== undefined) {
                        this[pName] = ret;
                    }

                    return this;
                };
            }

            if (!(getter in prototype) &amp;&amp; !data.hasOwnProperty(getter)) {
                data[getter] = function() {
                    return this[pName];
                };
            }
        });

        Ext.Object.merge(prototype.config, data.config);
        delete data.config;
    });

    Class.setDefaultPreprocessors(['extend', 'statics', 'inheritableStatics', 'mixins', 'config']);

    // Backwards compatible
    Ext.extend = function(subclass, superclass, members) {
        if (arguments.length === 2 &amp;&amp; Ext.isObject(superclass)) {
            members = superclass;
            superclass = subclass;
            subclass = null;
        }

        var cls;

        if (!superclass) {
            Ext.Error.raise(&quot;Attempting to extend from a class which has not been loaded on the page.&quot;);
        }

        members.extend = superclass;
        members.preprocessors = ['extend', 'mixins', 'config', 'statics'];

        if (subclass) {
            cls = new Class(subclass, members);
        }
        else {
            cls = new Class(members);
        }

        cls.prototype.override = function(o) {
            for (var m in o) {
                if (o.hasOwnProperty(m)) {
                    this[m] = o[m];
                }
            }
        };

        return cls;
    };

})();
</pre>
</body>
</html>
